# -*- coding: binary -*-

module Msf
  module Exploit::Remote::SMB::Server
    module Share
      module Command
        module Negotiate

          # Handles an SMB_COM_NEGOTIATE command, used by the client to initiate an
          # SMB connection between the client and the server.
          #
          # @param c [Socket] The client sending the request.
          # @param buff [String] The data including the client request.
          # @return [Integer] The number of bytes returned to the client as response.
          def smb_cmd_negotiate(c, buff)
            pkt = CONST::SMB_NEG_PKT.make_struct
            pkt.from_s(buff)

            dialects = pkt['Payload'].v['Payload'].gsub(/\x00/, '').split(/\x02/).grep(/^\w+/)
            dialect = dialects.index("NT LM 0.12") || dialects.length-1

            send_negotitate_res(c, {
              dialect: dialect,
              security_mode: CONST::NEG_SECURITY_PASSWORD,
              max_mpx: 50,
              max_vcs: 1,
              max_buff: 4356,
              max_raw: 65536,
              server_time_zone: 0,
              capabilities: CAPABILITIES,
              key_length: 8,
              key: Rex::Text.rand_text_hex(8)
            })
          end

          # Builds and sends an SMB_COM_CLOSE response.
          #
          # @param c [Socket] The client to answer.
          # @param opts [Hash{Symbol => <String, Integer>}] Response custom values.
          # @option opts [Integer] :dialect The index of the dialect selected by the server from the request.
          # @option opts [Integer] :security_mode Security modes supported or required by the server.
          # @option opts [Integer] :max_mpx The maximum number of outstanding SMB operations that the server supports.
          # @option opts [Integer] :max_vcs The maximum number of virtual circuits between the client and the server.
          # @option opts [Integer] :max_buff Largest SMB message that the server can handle.
          # @option opts [Integer] :max_raw Max size for SMB_COM_WRITE_RAW requests and SMB_COM_READ_RAW responses.
          # @option opts [Integer] :server_time_zone The server's time zone.
          # @option opts [Integer] :capabilities The server capability indicators.
          # @option opts [Integer] :key_length The challenge length.
          # @option opts [String] :key The challenge.
          # @return [Integer] The number of bytes returned to the client as response.
          def send_negotitate_res(c, opts = {})
            dialect = opts[:dialect] || 0
            security_mode = opts[:security_mode] || 0
            max_mpx = opts[:max_mpx] || 0
            max_vcs = opts[:max_vcs] || 0
            max_buff = opts[:max_buff] || 0
            max_raw = opts[:max_raw] || 0
            server_time_zone = opts[:server_time_zone] || 0
            capabilities = opts[:capabilities] || 0
            key_length = opts[:key_length] || 0
            key = opts[:key] || ''

            pkt = CONST::SMB_NEG_RES_NT_PKT.make_struct
            smb_set_defaults(c, pkt)

            pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NEGOTIATE
            pkt['Payload']['SMB'].v['Flags1'] = FLAGS
            pkt['Payload']['SMB'].v['Flags2'] = FLAGS2
            pkt['Payload']['SMB'].v['WordCount'] = CONST::SMB_NEGOTIATE_RES_WORD_COUNT
            pkt['Payload'].v['Dialect'] = dialect
            pkt['Payload'].v['SecurityMode'] = security_mode
            pkt['Payload'].v['MaxMPX'] = max_mpx
            pkt['Payload'].v['MaxVCS'] = max_vcs
            pkt['Payload'].v['MaxBuff'] = max_buff
            pkt['Payload'].v['MaxRaw'] = max_raw
            pkt['Payload'].v['SystemTimeLow'] = lo
            pkt['Payload'].v['SystemTimeHigh'] = hi
            pkt['Payload'].v['ServerTimeZone'] = server_time_zone
            pkt['Payload'].v['SessionKey'] = 0
            pkt['Payload'].v['Capabilities'] = capabilities
            pkt['Payload'].v['KeyLength'] = key_length
            pkt['Payload'].v['Payload'] = key

            c.put(pkt.to_s)
          end
        end
      end
    end
  end
end
